using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Newtonsoft.Json.Converters;
using System.Web;
using System.Text.RegularExpressions;
using System.Linq;

namespace Utils
{
    /// <summary>
    /// 日志操作类
    /// </summary>
    public class LogUtils
    {

        private static object locker = new object();
        /// <summary>
        /// 获取请求相关信息
        /// </summary>
        /// <param name="level">日志级别</param>
        /// <returns></returns>
        private static Dictionary<string, object> GetRequestData(LogLevel level)
        {
            Dictionary<string, object> dictData = new Dictionary<string, object>();            
            if (HttpContext.Current != null && HttpContext.Current.Request != null)
            {
               HttpRequest Request = HttpContext.Current.Request;
                if (Request.Url != null)
                {
                    dictData.Add("URI", Request.Url.AbsoluteUri);
                }
                if (Request.UrlReferrer != null)
                {
                    dictData.Add("UrlReferrer", Request.UrlReferrer.AbsoluteUri);
                }
                if (Request.ServerVariables != null)
                {
                    #region 获取IP地址
                    Regex regex = new Regex("([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}");
                    string str_Ip = string.Empty;
                    if (!string.IsNullOrEmpty(Request.ServerVariables["REMOTE_ADDR"]))
                    {
                        str_Ip = Request.ServerVariables["REMOTE_ADDR"];
                    }
                    else if (!string.IsNullOrEmpty(Request.ServerVariables["HTTP_CDN_SRC_IP"]) && regex.IsMatch(Request.ServerVariables["HTTP_CDN_SRC_IP"]))
                    {
                        str_Ip = Request.ServerVariables["HTTP_CDN_SRC_IP"];
                    }
                    else if (!string.IsNullOrEmpty(Request.ServerVariables["HTTP_X_FORWARDED_FOR"]) && regex.IsMatch(Request.ServerVariables["HTTP_X_FORWARDED_FOR"]))
                    {
                        str_Ip = Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
                    }
                    string[] ip = str_Ip.Split(',');
                    #endregion
                    dictData.Add("IP", ip[0]);
                }
                dictData.Add("UserAgent", Request.UserAgent);
                if (level == LogLevel.Warn)
                {
                    dictData.Add("POST信息", Request.Form);
                }
                else if (level == LogLevel.Error)
                {
                    dictData.Add("POST信息", Request.Form);

                    if (Request.Cookies != null && Request.Cookies.Count > 0)
                    {
                        Dictionary<string, object> dictTemp = new Dictionary<string, object>();
                        foreach (string key in Request.Cookies.AllKeys)
                        {
                            string value = Request.Cookies[key].Values[null];                            
                            if (Request.Cookies[key].Values.Count > 1)// Request.Cookies[key].Values => System.Web.HttpValueCollection 类型
                            {
                                Request.Cookies[key].Values.Remove(null);
                                Dictionary<string, string> kvDict = new Dictionary<string, string>();
                                kvDict.Add("@null", value);
                                var nvcTemp = Request.Cookies[key].Values;                                
                                foreach (var ckKey in nvcTemp.AllKeys)
                                {
                                    kvDict.Add(ckKey, nvcTemp[ckKey]);
                                }                                
                                dictTemp.Add(key, kvDict);
                            }
                            else
                            {
                                dictTemp.Add(key, value);
                            }
                        }
                        dictData.Add("Cookie信息", dictTemp);
                    }
                }
            }
            return dictData;
        }

        /// <summary>
        /// 写入日志
        /// </summary>
        /// <param name="logName">日志名称</param>
        /// <param name="developer">开发记录者</param>
        /// <param name="level">日志级别</param>
        /// <param name="detail">日志详情</param>
        /// <param name="Added">记录时间</param>
        private static void Write(string logName, Developer developer, LogLevel level, Dictionary<string,object> dictDetail, DateTime Added)
        {
            Log log = new Log();
            log.LogName = logName;
            log.Level = level;
            log.Developer = developer;
            if (HttpContext.Current != null && HttpContext.Current.Server != null)
            {
                log.ServerInfo = HttpContext.Current.Server.MachineName;
            }
            log.Added = Added;
            log.Detail = dictDetail.Union(GetRequestData(level)).ToDictionary(k => k.Key, v => v.Value);

            //todo :可以将日志写入 文件、数据库、MongoDB
            //这里写入根目录 log文件夹
            string logText = GetJsonFromObject(log) + "\r\n";
            string fileName = DateTime.Now.ToString("yyyyMMdd") + ".log";
            string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log");
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            fileName = Path.Combine(dir, fileName);
            File.AppendAllText(fileName, logText, Encoding.UTF8);
            File.AppendAllText(fileName, "--------------------------------------------------------------------------------------------------------------------------------\r\n", Encoding.UTF8);
        }

        /// <summary>
        /// 写入Info 日志
        /// </summary>
        /// <param name="logName">日志名称</param>
        /// <param name="developer">开发记录者</param>
        /// <param name="Info_objs">日志内容</param>
        public static void LogInfo(string logName, Developer developer, params object[] Info_objs)
        {
            lock (locker)
            {
                Dictionary<string, object> dictDetails = new Dictionary<string, object>();
                if (Info_objs != null && Info_objs.Length > 0)
                {
                    dictDetails.Add("Info", Info_objs);
                }
                Write(logName, developer, LogLevel.Info, dictDetails, DateTime.Now);
            }
        }


        /// <summary>s
        /// 写入带 堆栈执行 的Info 日志
        /// </summary>
        /// <param name="logName">日志名称</param>
        /// <param name="developer">开发记录者</param>
        /// <param name="Info_objs">日志内容</param>
        public static void LogWrite(string logName, Developer developer, params object[] Info_objs)
        {
            lock (locker)
            {
                Dictionary<string, object> dictDetails = new Dictionary<string, object>();
                System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace(1, true);
                System.Diagnostics.StackFrame frame = stack.GetFrame(0);
                string execFile = frame.GetFileName();
                string fullName = frame.GetMethod().DeclaringType.FullName;
                string methodName = frame.GetMethod().Name;
                int execLine = frame.GetFileLineNumber();
                dictDetails.Add("文件路径", execFile);
                dictDetails.Add("类全命名", fullName);
                dictDetails.Add("执行方法", methodName);
                dictDetails.Add("当前行号", execLine);

                if (Info_objs != null && Info_objs.Length > 0)
                {
                    dictDetails.Add("Info", Info_objs);
                }
                Write(logName, developer, LogLevel.Info, dictDetails, DateTime.Now);
            }
        }

        /// <summary>
        /// 写入Warn 日志
        /// </summary>
        /// <param name="logName">日志名称</param>
        /// <param name="developer">开发记录者</param>
        /// <param name="Info_objs">日志内容</param>
        public static void LogWarn(string logName, Developer developer, params object[] Info_objs)
        {
            lock (locker)
            {
                Dictionary<string, object> dictDetails = new Dictionary<string, object>();
                System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace(1, true);
                System.Diagnostics.StackFrame frame = stack.GetFrame(0);
                string execFile = frame.GetFileName();
                string fullName = frame.GetMethod().DeclaringType.FullName;
                string methodName = frame.GetMethod().Name;
                int execLine = frame.GetFileLineNumber();
                dictDetails.Add("文件路径", execFile);
                dictDetails.Add("类全命名", fullName);
                dictDetails.Add("执行方法", methodName);
                dictDetails.Add("当前行号", execLine);

                if (Info_objs != null && Info_objs.Length > 0)
                {
                    dictDetails.Add("Info", Info_objs);
                }
                Write(logName, developer, LogLevel.Warn, dictDetails, DateTime.Now);
            }
        }

        /// <summary>
        /// 写入 Errorr日志
        /// </summary>
        /// <param name="logName">日志名称</param>
        /// <param name="developer">开发记录者</param>
        /// <param name="ex">异常对象(可为null)</param>
        /// <param name="ext_InfoObjs">日志内容</param>
        public static void LogError(string logName, Developer developer, Exception ex, params object[] ext_InfoObjs)
        {
            lock (locker)
            {
                Dictionary<string, object> dictDetails = new Dictionary<string, object>();
                if (ex != null)
                {
                    dictDetails.Add("Exception", ex);
                }
                if (ex.InnerException != null)
                {
                    dictDetails.Add("InnerException", ex.InnerException);
                }               
                if (ext_InfoObjs != null && ext_InfoObjs.Length > 0)
                {
                    dictDetails.Add("Ext_Info", ext_InfoObjs);
                }
                Write(logName, developer, LogLevel.Error, dictDetails, DateTime.Now);
            }
        }


        #region  private 反射 对象

        private static string GetJsonFromObject(object obj, bool isFormat = true)
        {
            if (obj == null)
            {
                return string.Empty;
            }
            string result = string.Empty;

            //序列化 对象成Json字符串
            Newtonsoft.Json.Formatting format = isFormat ? Newtonsoft.Json.Formatting.Indented : Newtonsoft.Json.Formatting.None;

            //时间格式化
            IsoDateTimeConverter dtConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss:fff" };
            //枚举类型格式化
            StringEnumConverter emConverter = new StringEnumConverter();

            List<Newtonsoft.Json.JsonConverter> lstJCvtr = new List<Newtonsoft.Json.JsonConverter>();
            lstJCvtr.Add(dtConverter);
            lstJCvtr.Add(emConverter);

            result = Newtonsoft.Json.JsonConvert.SerializeObject(obj, format, lstJCvtr.ToArray());

            return result;
        }

        #endregion
    }


    /// <summary>
    /// 程序日志
    /// </summary>
    public class Log
    {
        public Guid Id { get { return Guid.NewGuid(); } }

        /// <summary>
        /// 日志名称
        /// </summary>
        public string LogName { get; set; }

        /// <summary>
        /// 日志级别
        /// </summary>
        public LogLevel Level { get; set; }

        /// <summary>
        /// 当前记录日志者
        /// </summary>
        public Developer Developer { get; set; }

        /// <summary>
        /// 服务器信息（如：服务器名称、服务器IP）
        /// </summary>
        public string ServerInfo { get; set; }

        /// <summary>
        /// 日志详细内容
        /// </summary>
        public dynamic Detail { get; set; }

        /// <summary>
        /// 日志时间
        /// </summary>     
        public DateTime Added { get; set; }

    }

    /// <summary>
    /// 日志级别
    /// </summary>
    public enum LogLevel
    {
        Info = 0,
        Warn = 1,
        Error = 2
    }

    /// <summary>
    /// 日志记录开发者
    /// </summary>
    public enum Developer
    {
        /// <summary>
        /// 系统默认
        /// </summary>
        SysDefault = 0,

        /// <summary>
        /// 其他用户
        /// </summary>
        MJ = 115
    }

}